﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SuperKit
{
    public struct ImageSameData {
        public float same;
        public ImageSameFile item;

        public bool Enable => item != null;

        public ImageSameData(float same, ImageSameFile item) {
            this.same = same;
            this.item = item;
        }
    }

    public class ImageSameGroupItem {

        public Size Size => Leader.size;
        public ImageSameFile Leader { get; }
        public float SameRate { get; }
        public string Name { get; }
        public int Count => Items.Count;
        public List<ImageSameData> Items { get; set; }
        public int Index { get; }
        public FileInfo Info { get; }

        public ImageSameGroupItem(int index, ImageSameFile leader, float sameRate) {
            Leader = leader;
            SameRate = sameRate;
            Items = new List<ImageSameData>();
            Index = index;
            Name = string.Format("[{0:D2}]{1}", Index, FileNameGroup.GetStartSplitName(leader.Name));
        }

        public void Add(ImageSameFile file) {
            Items.Add(new ImageSameData(file.Same, file));
        }

        public bool IsSameSize(Size size) {
            return Size.Width == size.Width && Size.Height == size.Height;
        }

        /// <summary>给定一个相似比较的比率，如果双方的大小相差不超过这个比率，则视为大小相同</summary>
        public bool IsSameSize(Size size, float rate) {
            if (IsSameSize(size)) {
                return true;
            }
            return ImageSameGroup.GetSizeSame(this.Size, size) >= rate;
        }

        public bool IsSame(ImageSameFile item, bool ignoreSize) {
            if (ignoreSize == false) {
                if (!IsSameSize(item.size)) {
                    return false;
                }
            }

            item.Same = Leader.GetCompare(item);
            return item.Same > SameRate;
        }
    }

    public class ImageSameFile : ThreadToolItem {
        public const int THUMB_SIZE = 50;
        public const int DEFAULT_COLOR_DIFF = 8;
        public const string INIT_DATA = "InitData";
        public const string COMPARE_DATA = "CompareData";

        public Size size;
        public ImageSameGroup Parent { get; }
        public FileInfo file { get; set; }
        public byte[] data;
        public string Name { get; }
        /// <summary>最后一次比较时得到的Same比率</summary>
        public float Same { get; set; }

        public ImageSameFile(ImageSameGroup parent, FileInfo file) {
            this.Parent = parent;
            this.file = file;
            size = Size.Empty;
            Name = Path.GetFileNameWithoutExtension(file.Name);
        }

        public override void Process() {
            if (IsCommand(INIT_DATA)) {
                InitData();
            }else if (IsCommand(COMPARE_DATA)) {
                CompareData();
            }
        }

        public override string GetStartMessage() {
            if (IsCommand(INIT_DATA)) {
                return string.Format("[{0}/{1}]初始化：{2}", ThreadIndex, Parent.Count, file.Name);
            } else if (IsCommand(COMPARE_DATA)) {
                return string.Format("[{0}/{1}]比较：{2}", ThreadIndex, Parent.Count, file.Name);
            }
            return base.GetStartMessage();
        }

        public void CompareData() {
            Parent.AddTo(this);
        }

        private void InitData() {
            if (!Kit.IsFileType(file, Kit.PROCESSED_IMG_EXTS)) {
                size = Size.Empty;
                data = new byte[size.Width * size.Height];
                return;
            }

            CreateThumb();
        }

        public override string ToString() {
            return file.Name;
        }

        public void CreateThumb() {
            data = ImageSameGroup.CreateCompareData(file.FullName, THUMB_SIZE, THUMB_SIZE, out size);
        }


        public void Release() {
            data = null;
        }


        /// <summary>
        /// 比较两个项目，返回相似率
        /// </summary>
        /// <param name="obj">比较的目标对象</param>
        /// <param name="colorDiff">色差范围，在色差的多少范围之中算相似。</param>
        /// <returns>相似率，0-1范围之间的值</returns>
        unsafe public float GetCompare(ImageSameFile obj, int colorDiff = DEFAULT_COLOR_DIFF) {
            // 得到总数值项
            float same = 0;

            for (int i = 0; i < data.Length; i++) {
                var diff = data[i] - obj.data[i];
                if (diff < 0) {
                    diff = diff * -1;
                }

                if (diff < colorDiff) {
                    same++;
                }
            }

            return (same / (float)data.Length);
        }
    }

    public class ImageSameGroup {

        public FileGroup Group { get; }
        public int Count => Group.Count;
        public List<ImageSameGroupItem> Items { get; }
        public ThreadTool<ImageSameFile> Files { get; }
        public float SameRate { get; private set; }
        public bool IgnoreSize { get; }
        public GeneralOutput Output { get; }

        public ImageSameGroup(FileGroup group, float sameRate, bool ignoreSize, int threadCount, GeneralOutput output) {
            Group = group;
            SameRate = sameRate;
            IgnoreSize = ignoreSize;
            Output = output;
            Items = new List<ImageSameGroupItem>();
            Files = new ThreadTool<ImageSameFile>(threadCount, output);
            foreach (var i in Group.Files) {
                Files.Add(new ImageSameFile(this, i));
            }
        }

        public void Analysis() {
            Files.Start(ImageSameFile.INIT_DATA);
            Files.StartSequence(ImageSameFile.COMPARE_DATA);


        }



        public void AddTo(ImageSameFile item) {
            
            var group = GetGroup(item);
            if (group == null) {
                lock(Items) {
                    group = CreateNewItem(item);
                }
            }
            group.Add(item);
        }

        
        private ImageSameGroupItem CreateNewItem(ImageSameFile item) {
            ImageSameGroupItem group = new ImageSameGroupItem(Items.Count, item, SameRate);
            Items.Add(group);
            return group;
        }

        private ImageSameGroupItem GetGroup(ImageSameFile item) {
            foreach (var i in Items) {
                if (i.IsSame(item, IgnoreSize)) {
                    return i;
                }
            }
            return null;
        }

        public static float GetSizeSame(Size a, Size b) {
            float widthDiff = Math.Abs(a.Width - b.Width);
            float heightDiff = Math.Abs(a.Height - b.Height);
            float widthRate = 1.0f - (widthDiff / Math.Max(a.Width, b.Width));
            float heightRate = 1.0f - (heightDiff / Math.Max(a.Height, b.Height));
            return (widthRate + heightRate) * 0.5f;
        }

        unsafe public static byte[] CreateCompareData(string fileName, int width, int height, out Size size) {
            Image img = null;
            try {
                img = Image.FromFile(fileName);
                size = img.Size;
            } catch (IOException) {
                // 如果出现IO错误，可能是因为对方的文件有问题，设置一个假的项目
                size = Size.Empty;
                return new byte[width * height];
            }

            Bitmap bmp = new Bitmap(img, width, height);

            Rectangle rect = new Rectangle(0, 0, width, height);
            var tmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            int count = width * height;
            int dataSize = count * 3; // Format24bppRgb使用3个字节
            byte* p1 = (byte*)tmpData.Scan0;
            byte[] data = new byte[dataSize];
            for (int i = 0; i < dataSize; i++) {
                data[i] = p1[i];
            }

            bmp.UnlockBits(tmpData);
            bmp.Dispose();
            img.Dispose();

            return data;
        }
    }
}
